# -*- coding: utf-8 -*-
"""
Created on Mon Jan 22 16:56:58 2018

@author:Devin

"""
import pandas as pd
import os
import numpy as np
from tqdm import tqdm
from multiprocessing import Pool
#from mix_class import Construction_data
from sklearn.preprocessing import Imputer
from sklearn.externals import joblib
from collections import Counter
import copy
import math
import hashlib
import pickle
import json
import time
import temp_data_load
# =============================================================================
# Define a class for detect time
# =============================================================================
class Battery_Time_Detect():
    '''
    This is a class package for battery error time detect use already trained 2-class-model default RF.
    two key parameter need: data and model path
    df:list_df element very highly suggest DataFrame,if not,please assign columns and index
    model path: the path where model saved, default pkl format
    base_sort_path:base path for differ days
    col:if data format is not df, columns
    indexs:if data format is not df, index
    result_path: dict result path,json
    result_format:'json' or 'csv'
    multiprocessing: int,process number,or None

    ''
    lap:预测窗口长度，在该窗口内统计01个数
    gap:滑窗间隔，默认为lap，不进行重复计算
    pp: 滑窗内01区分阈值，默认1的个数大于10%认为故障
    min_pre_gap:预测时间取滑窗内最后一条，与真实故障时间之差的最小值
    max_pre_gap:预测时间取滑窗内最后一条，与真实故障时间之差的最大值
    ''

    '''
    def __init__(self,df,model_path,base_sort_path,result_path = 'predict_result/',
                     result_format='json',multiprocessing=None,col=None,
                     indexs=None,lap=10,gap=10,pp=0.1,
                     min_pre_gap=0,max_pre_gap=90,periods = 'D'):
        if not os.path.exists(model_path):
            print('model path not exist !')
            return
        else:
            model_paths = os.path.join(model_path,os.listdir(model_path)[0])
        if not type(df) == list:
            print('df should be a list which element is df !')
            return
        if not os.path.exists(result_path):
            os.makedirs(result_path)

        self.df_list = df
        self.col = col
        self.indexs = indexs
        self.lap = lap
        self.gap = gap
        self.pp = pp
        self.min_pre_gap = min_pre_gap
        self.max_pre_gap = max_pre_gap
        self.periods = periods
        self.model = joblib.load(model_paths)
        self.cache_root_dir = result_path
        self.base_sort_path = base_sort_path
        self.result_format = result_format
        self.multiprocessing = multiprocessing
        self.use_colsl = ['15_minutes_st', 'ac_auto_on_count', 'ac_on_count', 'avg_run_times',
                           'avg_temperature_in', 'avg_temperature_out', 'batt_0', 'batt_1',
                           'battery_avg', 'battery_max', 'battery_min', 'battery_mix1',
                           'battery_mix4', 'battery_start', 'battery_var', 'brakepos_var',
                           'c_temp_avg', 'cool_avg', 'd_temp_avg', 'dip_light_count',
                           'dr_avg_mile', 'dr_avg_v', 'dr_mile50', 'dr_v0_rate', 'fanspeed_avg',
                           'fanspeed_var', 'kurt_v_battery', 'main_light_count', 'mils',
                           'n_power_time', 'num_non_p', 'oli_box_avg',
                           'remote_boot_times', 'run_time_2k', 'side_light_count',
                           'skew_v_battery', 'starting_num_day', 'wiperswitch_avg', 'x_acc',
                           'y_acc', 'z_acc']
        self.base = self._load_base()
        self.temp_data_list = self._combin_data()
        if self.result_format != 'json' and self.result_format != 'csv':
            print('Give right parameter of result_format:json or csv');return
        if self.multiprocessing != None and type(self.multiprocessing) != int:
            print('Give right parameter of multiprocessing:None or int format ');return
        if self.multiprocessing == None:
            self.single_pool_result()
        else:
            self.mult_pool_result()

#    md5压缩
    def _md5(self,s):
        m = hashlib.md5()
        m.update(s.encode("utf8"))
        return m.hexdigest()
#    加载预测天数范围基准
    def _load_base(self):
        path = os.path.join(self.base_sort_path,'%s.dump' % self._md5('base'))
        with open(path, 'rb') as fr:
            base = pickle.load(fr)
        return base


#    数据接入处理
    def _combin_data(self):
        '''
        数据抽取
        数据聚合
        pass

        '''
#    对数据滑窗，默认窗口期7天，小于等于7直接处理，大于7进行切分
    def _get_moving_data(self,window_dataframe):
        rows = window_dataframe.shape[0]
        window_num = math.floor(rows/self.gap)
        index = 0
        result_list = []
        for i in range(window_num):
            tran_window_data = window_dataframe.iloc[index:index+self.lap,:]
            if tran_window_data.shape[0] == self.lap:
                result_list.append(tran_window_data)
            index += self.gap
            if index > rows:
                break
        return result_list
#    统计滑窗内01数量 与给定阈值比较
    def _count_01(self,df):
        pre_list = list(df.loc[:,'pre_label']);values_counts = Counter(pre_list)
        if (values_counts[1]/len(pre_list)) >= self.pp:
            return 1
        else:
            return 0
#    预测入口
    def _pre_df(self,df):
        if df.shape[0]<=self.lap:
            v = self._count_01(df)
            return v,df.index[0]
        else:
            for item in self._get_moving_data(df):
                v2 = self._count_01(item)
                if v2==1:
                    break
                elif v2==0:
                    continue
            return v2,item.index[0]
#    计算与基准天数的距离，归一化后输出
    def _distence(self,points):
        distence_list=[];r_dict={}
        for i in self.base:
            sim = [1.0 / (1.0 + (np.linalg.norm(np.mean(points) - x))) for x in self.base[i]]
            distence_list.append([i,max(sim)])
        sums = sum([z[1] for z in distence_list])
        for x in distence_list:
            r_dict[x[0]] = x[1]/sums
        return r_dict
        '''
        max_d = max([x[1] for x in distence_list])
        for x in distence_list:
            if x[1]==max_d:
                return x[0],x[1]/sum([z[1] for z in distence_list])
        '''
#    多进程入口
    def final_test_mult_pool(self):
        result_list = []
        pool = Pool(processes=self.multiprocessing);pool_list = []
        for item in self.df_list:
            result_p = pool.apply_async(self.one_df_result,(item,))
            pool_list.append(result_p)
        pool.close()#关闭进程池，不再接受新的进程
        pool.join()#主进程阻塞等待子进程的退出
        for result in pool_list:
            one_result = result.get()
            print(one_result)
            if one_result !=0:
                result_list.append(one_result)
        return result_list
#   结果保存
    def result_save(self,result):
        name_time = time.strftime('%Y-%m-%d_%H_%M',time.localtime(time.time()))
        result_json = {}
        if self.result_format == 'csv':
            temp = pd.DataFrame(result,columns=['vin','warning_time','15','1530','3060','6080'])
            temp.to_csv(os.path.join(self.cache_root_dir,'record_'+str(name_time)+".csv"))
        elif self.result_format == 'json':
            for item in result:
                result_json[item[0]] = {item[1]:{15:item[2],1530:item[3],3060:item[4],6080:item[5]}}
            with open(os.path.join(self.cache_root_dir,'record_'+str(name_time)+".json"),"w") as f:
                json.dump(result_json,f)
#    一个df的预测结果
    def one_df_result(self,df):
        pre = list(self.model.predict(df.loc[:,self.use_colsl]))
        tem = copy.deepcopy(df);tem['pre_label'] = pre
        v,t = self._pre_df(tem)
        if v == 1:
            pvin = df.vin[0]
            p_result = self._distence(df)
            return [pvin,str(t),p_result[15],p_result[1530],p_result[3060],p_result[6080]]
        elif v == 0:
            return 0
#    预测入口
    def final_predict(self):
        results = []
        for item in self.df_list:
            re = self.one_df_result(item)
            if re !=0:
                results.append(re)
        return results
#    单进程
    def single_pool_result(self):
        res = self.final_predict()
        self.result_save(res)
        return
#    多进程入口
    def mult_pool_result(self):
        res = self.final_test_mult_pool()
        self.result_save(res)
        return



if __name__ == '__main__':
    trainx, testlist, testx, emptylist, e_list, err_e_list, err_n_list, temp_vin = temp_data_load.load_data()
    s = time.time()
    Battery_Time_Detect(df = testlist[0:10],model_path = 'model',base_sort_path = 'base',
                        multiprocessing=None,result_path = 'predict_result/',result_format='csv')
    efor = time.time()
    print("for cost time ：", int(efor - s))
